use casbin::{error::AdapterError, Error as CasbinError, Filter};
use sea_orm::sea_query::Condition;
use sea_orm::ActiveValue::NotSet;
use sea_orm::{
    ColumnTrait, ConnectionTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, Schema, Set,
    TransactionTrait,
};

use crate::model::{self, NewCasbinRule};

pub(crate) async fn clear_policy(conn: &DatabaseConnection) -> casbin::Result<()> {
    model::Entity::delete_many()
        .exec(conn)
        .await
        .map_err(|err| CasbinError::from(AdapterError(Box::new(err))))?;
    Ok(())
}

pub(crate) async fn create_table(conn: &DatabaseConnection) -> Result<(), DbErr> {
    let builder = conn.get_database_backend();
    let schema = Schema::new(builder);
    let stmt = builder.build(&schema.create_table_from_entity(model::Entity));
    conn.execute(stmt).await?;
    Ok(())
}

pub(crate) async fn load_policy(conn: &DatabaseConnection) -> casbin::Result<Vec<model::Model>> {
    let casbin_rule = model::Entity::find()
        .all(conn)
        .await
        .map_err(|err| CasbinError::from(AdapterError(Box::new(err))))?;
    Ok(casbin_rule)
}

pub(crate) async fn load_filtered_policy<'a>(
    conn: &DatabaseConnection,
    filter: &Filter<'a>,
) -> casbin::Result<Vec<model::Model>> {
    let (g_filter, p_filter) = filtered_where_values(filter);
    let casbin_rule = model::Entity::find()
        .filter(
            Condition::any()
                .add(
                    Condition::all()
                        .add(model::Column::Ptype.like("p%"))
                        .add(model::Column::V0.contains(g_filter[0]))
                        .add(model::Column::V1.contains(g_filter[1]))
                        .add(model::Column::V2.contains(g_filter[2]))
                        .add(model::Column::V3.contains(g_filter[3]))
                        .add(model::Column::V4.contains(g_filter[4]))
                        .add(model::Column::V5.contains(g_filter[5])),
                )
                .add(
                    Condition::all()
                        .add(model::Column::Ptype.like("g%"))
                        .add(model::Column::V0.contains(p_filter[0]))
                        .add(model::Column::V1.contains(p_filter[1]))
                        .add(model::Column::V2.contains(p_filter[2]))
                        .add(model::Column::V3.contains(p_filter[3]))
                        .add(model::Column::V4.contains(p_filter[4]))
                        .add(model::Column::V5.contains(p_filter[5])),
                ),
        )
        .all(conn)
        .await
        .map_err(|err| CasbinError::from(AdapterError(Box::new(err))))?;
    Ok(casbin_rule)
}

fn filtered_where_values<'a>(filter: &Filter<'a>) -> ([&'a str; 6], [&'a str; 6]) {
    let mut g_filter: [&'a str; 6] = ["%", "%", "%", "%", "%", "%"];
    let mut p_filter: [&'a str; 6] = ["%", "%", "%", "%", "%", "%"];
    for (idx, val) in filter.g.iter().enumerate() {
        if val != &"" {
            g_filter[idx] = val;
        }
    }
    for (idx, val) in filter.p.iter().enumerate() {
        if val != &"" {
            p_filter[idx] = val;
        }
    }
    (g_filter, p_filter)
}

pub(crate) async fn save_policy(
    conn: &DatabaseConnection,
    rules: Vec<NewCasbinRule<'_>>,
) -> casbin::Result<()> {
    let txn = conn
        .begin()
        .await
        .map_err(|err| CasbinError::from(AdapterError(Box::new(err))))?;

    model::Entity::delete_many()
        .exec(&txn)
        .await
        .map_err(|err| CasbinError::from(AdapterError(Box::new(err))))?;

    let actives = rules.iter().map(|c| model::ActiveModel {
        id: NotSet,
        ptype: Set(c.ptype.to_string()),
        v0: Set(c.v0.to_string()),
        v1: Set(c.v1.to_string()),
        v2: Set(c.v2.to_string()),
        v3: Set(c.v3.to_string()),
        v4: Set(c.v4.to_string()),
        v5: Set(c.v5.to_string()),
    });
    model::Entity::insert_many(actives)
        .exec(&txn)
        .await
        .map_err(|err| CasbinError::from(AdapterError(Box::new(err))))?;
    txn.commit()
        .await
        .map_err(|err| CasbinError::from(AdapterError(Box::new(err))))?;
    Ok(())
}

pub(crate) async fn add_policy(
    conn: &DatabaseConnection,
    rule: NewCasbinRule<'_>,
) -> casbin::Result<bool> {
    Ok(true)
}

pub(crate) async fn add_policies(
    conn: &DatabaseConnection,
    rules: Vec<NewCasbinRule<'_>>,
) -> casbin::Result<bool> {
    Ok(true)
}

pub async fn remove_policy(
    conn: &DatabaseConnection,
    pt: &str,
    rule: Vec<String>,
) -> casbin::Result<bool> {
    Ok(true)
}

pub async fn remove_policies(
    conn: &DatabaseConnection,
    pt: &str,
    rules: Vec<Vec<String>>,
) -> casbin::Result<bool> {
    Ok(true)
}

pub async fn remove_filtered_policy(
    conn: &DatabaseConnection,
    pt: &str,
    field_index: usize,
    field_values: Vec<String>,
) -> casbin::Result<bool> {
    Ok(true)
}
